#ifndef QUERYPLAN_H_
#define QUERYPLAN_H_

#include "Record.h"
#include "Schema.h"
#include "Comparison.h"
#include "Function.h"
#include "DBFile.h"
#include "RelOp.h"
#include <string>
#include <iostream>
#include <map>

#define PIPE_SIZE 100
#define USE_PAGES 100

using namespace std;

class OperatorNode
{
	public:
		//pipe and file for each node are common,put it in map
		int  InPipe, OutPipe;
		//global pipe map
		static map<int, Pipe*> Pipes;


		OperatorNode * left;
		OperatorNode * right;

		OperatorNode () : InPipe(-1), OutPipe(-1),left(NULL), right(NULL)
	{
	}

		//  in-order traversal for printing
		virtual void PrintNode() {}
		// Will be post order traversal for execution
		virtual void ExecutePostOrder() {}

		virtual void ExecuteNode() {}
		virtual ~OperatorNode() {} 
};

class SelectPipeNode : public OperatorNode 
{
	public:
		CNF*  cnf;
		Record * literal;

		SelectPipeNode (int in, int out, CNF* myCnf,  Record* inpLit) 
		{
			InPipe = in;
			OutPipe = out;
			cnf = myCnf;
			literal = inpLit;
			//put in map for the outpipe value with 100 size
			OperatorNode::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~SelectPipeNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();

};

class SelectFileNode : public OperatorNode 
{
	public:
		CNF* cnf;
		Record * literal;
		string  InFileName;

		SelectFileNode (string inFile, int outPipe, CNF* myCnf, Record * inpLit) 
		{
			InFileName = inFile;
			OutPipe = outPipe;
			cnf = myCnf;
			literal = inpLit;
			OperatorNode ::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~SelectFileNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

class ProjectNode : public OperatorNode 
{
	public:
		int * atts;
		int  attsToKeep, totAtts, print;
		Schema * sch;

		ProjectNode (int inPipe, int outPipe, int *attsList, int attsKeep, int total, Schema * inpSch, int printFlag)
		{
			InPipe = inPipe;
			OutPipe = outPipe;
			atts = attsList;
			attsToKeep = attsKeep;
			totAtts = total;
			sch = inpSch;
			print = printFlag;
			OperatorNode ::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~ProjectNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

			if (atts)
			{
				delete [] atts; 
				atts= NULL;
			}
		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

class JoinNode : public OperatorNode 
{
	public:
		int rightInPipe; //right pipe for right relation
		CNF *cnf;
		Record * literal;
		int numAttsLeft,numAttsRight;

		JoinNode (int inpPipeL, int inpPipeR, int outPipe, CNF*  myCnf, Record * inpLit, int numLeft, int numRight)
		{
			InPipe = inpPipeL;
			rightInPipe = inpPipeR;
			OutPipe = outPipe;
			cnf = myCnf;
			literal= inpLit;
			numAttsLeft = numLeft;
			numAttsRight = numRight;
			OperatorNode::Pipes[OutPipe] = new Pipe(PIPE_SIZE); 
		}

		~JoinNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

class SumNode : public OperatorNode 
{
	public:
		Function * func;
		bool print;

		SumNode (int inpPipe, int outPipe, Function *myFunc, bool printFlag)
		{
			InPipe = inpPipe;
			OutPipe = outPipe;
			func = myFunc;
			print= printFlag;
			OperatorNode ::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~SumNode()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

class GroupByNode : public OperatorNode 
{
	public:
		Function * func;
		OrderMaker * order;
		Schema* tmpSchema;		
		int * numKeep;
		int numAttsKeep;
		int totAtts;

		GroupByNode (int inpPipe, int outPipe, Function *myFunc, OrderMaker *om,int * keep, Schema* tmpS, int numAttsk,int total)
		{
			InPipe = inpPipe;
			OutPipe = outPipe;
			func= myFunc;
			order= om;
			tmpSchema = tmpS;	
			numKeep=keep;
			numAttsKeep=numAttsk;
			totAtts=total;
			OperatorNode ::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~GroupByNode()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};


class DuplicateNode : public OperatorNode 
{
	public:
		Schema *sch;
		int print;

		DuplicateNode (int inpPipe, int outPipe, Schema * mySch, int printFlag)
		{
			InPipe = inpPipe;
			OutPipe = outPipe;
			sch= mySch;
			print= printFlag;
			OperatorNode ::Pipes[OutPipe] = new Pipe(PIPE_SIZE);
		}

		~DuplicateNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

class WriteOutNode : public OperatorNode 
{
	public:
		Schema * sch;
		string   OutFileName;

		WriteOutNode (int inpPipe, string outFile, Schema * mySch)
		{
			InPipe = inpPipe;
			OutFileName = outFile;
			sch= mySch;

		}

		~WriteOutNode ()
		{
			if (this->left)
				delete this->left;
			if (this->right)
				delete this->right;

		}

		void PrintNode();
		void ExecutePostOrder();
		void ExecuteNode();
};

#endif

